# partially based on django-tremenus code written by Julien Phalip
# (http://code.google.com/p/django-treemenus/)
#
# Copyright (c) 2008, Julien Phalip
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
#
#     * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
#     * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
#     * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

from django.test import TestCase

from djwheel.app.contenttree.models import Menu, MenuItem
from djwheel.app.contenttree.utils import move_item, clean_order, move_item_or_clean_order

class ContenttreeTestCase(TestCase):
    def test_delete(self):
        menu = Menu(name='menu_delete')
        menu.save()
        menu_item1 = MenuItem.objects.create(title='menu_item1', parent=menu.root_item)
        menu_item2 = MenuItem.objects.create(title='menu_item2', parent=menu.root_item)
        menu_item3 = MenuItem.objects.create(title='menu_item3', parent=menu_item1)
        menu_item4 = MenuItem.objects.create(title='menu_item4', parent=menu_item1)
        menu_item5 = MenuItem.objects.create(title='menu_item5', parent=menu_item1)
        menu_item6 = MenuItem.objects.create(title='menu_item6', parent=menu_item2)
        menu_item7 = MenuItem.objects.create(title='menu_item7', parent=menu_item4)
        menu_item8 = MenuItem.objects.create(title='menu_item8', parent=menu_item4)
        menu_item9 = MenuItem.objects.create(title='menu_item9', parent=menu_item1)
        menu_item10 = MenuItem.objects.create(title='menu_item10', parent=menu_item4)

        # menu
        #     ri
        #         mi1
        #             mi3
        #             mi4
        #                 mi7
        #                 mi8
        #                 mi10
        #             mi5
        #             mi9
        #         mi2
        #             mi6

        # Check initial order
        self.assertEquals(menu_item1.order, 0)
        self.assertEquals(menu_item2.order, 1)
        self.assertEquals(menu_item3.order, 0)
        self.assertEquals(menu_item4.order, 1)
        self.assertEquals(menu_item5.order, 2)
        self.assertEquals(menu_item6.order, 0)
        self.assertEquals(menu_item7.order, 0)
        self.assertEquals(menu_item8.order, 1)
        self.assertEquals(menu_item9.order, 3)
        self.assertEquals(menu_item10.order, 2)

        # Check initial levels
        self.assertEquals(menu_item1.level, 1)
        self.assertEquals(menu_item2.level, 1)
        self.assertEquals(menu_item3.level, 2)
        self.assertEquals(menu_item4.level, 2)
        self.assertEquals(menu_item5.level, 2)
        self.assertEquals(menu_item6.level, 2)
        self.assertEquals(menu_item7.level, 3)
        self.assertEquals(menu_item8.level, 3)
        self.assertEquals(menu_item9.level, 2)
        self.assertEquals(menu_item10.level, 3)

        # Delete some items
        menu_item8.delete()
        menu_item3.delete()

        # menu
        #     ri
        #         mi1
        #             mi4
        #                 mi7
        #                 mi10
        #             mi5
        #             mi9
        #         mi2
        #             mi6

        # Refetch items from db
        menu_item1 = MenuItem.objects.get(pk=menu_item1.pk)
        menu_item2 = MenuItem.objects.get(pk=menu_item2.pk)
        menu_item4 = MenuItem.objects.get(pk=menu_item4.pk)
        menu_item5 = MenuItem.objects.get(pk=menu_item5.pk)
        menu_item6 = MenuItem.objects.get(pk=menu_item6.pk)
        menu_item7 = MenuItem.objects.get(pk=menu_item7.pk)
        menu_item9 = MenuItem.objects.get(pk=menu_item9.pk)
        menu_item10 = MenuItem.objects.get(pk=menu_item10.pk)

        # Check order
        self.assertEquals(menu_item1.order, 0)
        self.assertEquals(menu_item2.order, 1)
        self.assertEquals(menu_item4.order, 0)
        self.assertEquals(menu_item5.order, 1)
        self.assertEquals(menu_item6.order, 0)
        self.assertEquals(menu_item7.order, 0)
        self.assertEquals(menu_item9.order, 2)
        self.assertEquals(menu_item10.order, 1)

        # Check levels
        self.assertEquals(menu_item1.level, 1)
        self.assertEquals(menu_item2.level, 1)
        self.assertEquals(menu_item4.level, 2)
        self.assertEquals(menu_item5.level, 2)
        self.assertEquals(menu_item6.level, 2)
        self.assertEquals(menu_item7.level, 3)
        self.assertEquals(menu_item9.level, 2)
        self.assertEquals(menu_item10.level, 3)

        # Delete some items
        menu_item4.delete()
        menu_item5.delete()

        # menu
        #     ri
        #         mi1
        #             mi9
        #         mi2
        #             mi6

        # Refetch items from db
        menu_item1 = MenuItem.objects.get(pk=menu_item1.pk)
        menu_item2 = MenuItem.objects.get(pk=menu_item2.pk)
        menu_item6 = MenuItem.objects.get(pk=menu_item6.pk)
        menu_item9 = MenuItem.objects.get(pk=menu_item9.pk)

        # Check order
        self.assertEquals(menu_item1.order, 0)
        self.assertEquals(menu_item2.order, 1)
        self.assertEquals(menu_item6.order, 0)
        self.assertEquals(menu_item9.order, 0)

        # Check levels
        self.assertEquals(menu_item1.level, 1)
        self.assertEquals(menu_item2.level, 1)
        self.assertEquals(menu_item6.level, 2)
        self.assertEquals(menu_item9.level, 2)

        # Check that deleted items are in fact, gone.
        self.assertRaises(MenuItem.DoesNotExist, lambda: MenuItem.objects.get(pk=menu_item3.pk))
        self.assertRaises(MenuItem.DoesNotExist, lambda: MenuItem.objects.get(pk=menu_item4.pk))
        self.assertRaises(MenuItem.DoesNotExist, lambda: MenuItem.objects.get(pk=menu_item5.pk))
        self.assertRaises(MenuItem.DoesNotExist, lambda: MenuItem.objects.get(pk=menu_item7.pk))
        self.assertRaises(MenuItem.DoesNotExist, lambda: MenuItem.objects.get(pk=menu_item8.pk))
        self.assertRaises(MenuItem.DoesNotExist, lambda: MenuItem.objects.get(pk=menu_item10.pk))

    def test_change_parents(self):
        menu = Menu(name='menu_change_parents')
        menu.save()
        menu_item1 = MenuItem.objects.create(title='menu_item1', parent=menu.root_item)
        menu_item2 = MenuItem.objects.create(title='menu_item2', parent=menu.root_item)
        menu_item3 = MenuItem.objects.create(title='menu_item3', parent=menu_item1)
        menu_item4 = MenuItem.objects.create(title='menu_item4', parent=menu_item1)
        menu_item5 = MenuItem.objects.create(title='menu_item5', parent=menu_item1)

        # menu
        #     ri
        #         mi1
        #             mi3
        #             mi4
        #             mi5
        #         mi2

        # Check initial order
        self.assertEquals(menu_item1.order, 0)
        self.assertEquals(menu_item2.order, 1)
        self.assertEquals(menu_item3.order, 0)
        self.assertEquals(menu_item4.order, 1)
        self.assertEquals(menu_item5.order, 2)

        # Check initial levels
        self.assertEquals(menu_item1.level, 1)
        self.assertEquals(menu_item2.level, 1)
        self.assertEquals(menu_item3.level, 2)
        self.assertEquals(menu_item4.level, 2)
        self.assertEquals(menu_item5.level, 2)

        # Change parent for some items
        menu_item4.parent = menu.root_item
        menu_item4.save()
        menu_item5.parent = menu_item2
        menu_item5.save()

        # menu
        #     ri
        #         mi1
        #             mi3
        #         mi2
        #             mi5
        #         mi4

        # Refetch items from db
        menu_item1 = MenuItem.objects.get(pk=menu_item1.pk)
        menu_item2 = MenuItem.objects.get(pk=menu_item2.pk)
        menu_item3 = MenuItem.objects.get(pk=menu_item3.pk)
        menu_item4 = MenuItem.objects.get(pk=menu_item4.pk)
        menu_item5 = MenuItem.objects.get(pk=menu_item5.pk)

        # Check order
        self.assertEquals(menu_item1.order, 0)
        self.assertEquals(menu_item2.order, 1)
        self.assertEquals(menu_item3.order, 0)
        self.assertEquals(menu_item4.order, 2)
        self.assertEquals(menu_item5.order, 0)

        # Check levels
        self.assertEquals(menu_item1.level, 1)
        self.assertEquals(menu_item2.level, 1)
        self.assertEquals(menu_item3.level, 2)
        self.assertEquals(menu_item4.level, 1)
        self.assertEquals(menu_item5.level, 2)

        # Change parent for some items
        menu_item2.parent = menu_item1
        menu_item2.save()
        menu_item5.parent = menu_item1
        menu_item5.save()
        menu_item3.parent = menu.root_item
        menu_item3.save()
        menu_item1.parent = menu_item4
        menu_item1.save()

        # menu
        #     ri
        #         mi4
        #             mi1
        #                 mi2
        #                 mi5
        #         mi3

        # Refetch items from db
        menu_item1 = MenuItem.objects.get(pk=menu_item1.pk)
        menu_item2 = MenuItem.objects.get(pk=menu_item2.pk)
        menu_item3 = MenuItem.objects.get(pk=menu_item3.pk)
        menu_item4 = MenuItem.objects.get(pk=menu_item4.pk)
        menu_item5 = MenuItem.objects.get(pk=menu_item5.pk)

        # Check order
        self.assertEquals(menu_item1.order, 0)
        self.assertEquals(menu_item2.order, 0)
        self.assertEquals(menu_item3.order, 1)
        self.assertEquals(menu_item4.order, 0)
        self.assertEquals(menu_item5.order, 1)

        # Check levels
        self.assertEquals(menu_item1.level, 2)
        self.assertEquals(menu_item2.level, 3)
        self.assertEquals(menu_item3.level, 1)
        self.assertEquals(menu_item4.level, 1)
        self.assertEquals(menu_item5.level, 3)

        # Change parent for some items
        menu_item2.parent = menu_item4
        menu_item2.save()
        menu_item4.parent = menu_item3
        menu_item4.save()
        menu_item1.parent = menu.root_item
        menu_item1.save()
        menu_item5.parent = menu_item4
        menu_item5.save()

        # menu
        #     ri
        #         mi3
        #             mi4
        #                 mi2
        #                 mi5
        #         mi1

        # Refetch items from db
        menu_item1 = MenuItem.objects.get(pk=menu_item1.pk)
        menu_item2 = MenuItem.objects.get(pk=menu_item2.pk)
        menu_item3 = MenuItem.objects.get(pk=menu_item3.pk)
        menu_item4 = MenuItem.objects.get(pk=menu_item4.pk)
        menu_item5 = MenuItem.objects.get(pk=menu_item5.pk)

        # Check order
        self.assertEquals(menu_item1.order, 1)
        self.assertEquals(menu_item2.order, 0)
        self.assertEquals(menu_item3.order, 0)
        self.assertEquals(menu_item4.order, 0)
        self.assertEquals(menu_item5.order, 1)

        # Check levels
        self.assertEquals(menu_item1.level, 1)
        self.assertEquals(menu_item2.level, 3)
        self.assertEquals(menu_item3.level, 1)
        self.assertEquals(menu_item4.level, 2)
        self.assertEquals(menu_item5.level, 3)


    def test_move_up(self):
        menu = Menu(name='menu_move_up')
        menu.save()
        menu_item1 = MenuItem.objects.create(title='menu_item1', parent=menu.root_item)
        menu_item2 = MenuItem.objects.create(title='menu_item2', parent=menu.root_item)
        menu_item3 = MenuItem.objects.create(title='menu_item3', parent=menu.root_item)
        menu_item4 = MenuItem.objects.create(title='menu_item4', parent=menu.root_item)

        self.assertEquals(menu_item1.order, 0)
        self.assertEquals(menu_item2.order, 1)
        self.assertEquals(menu_item3.order, 2)
        self.assertEquals(menu_item4.order, 3)

        move_item(menu_item3, -1) # Move up

        # Retrieve objects from db
        menu_item1 = MenuItem.objects.get(title='menu_item1', parent=menu.root_item)
        menu_item2 = MenuItem.objects.get(title='menu_item2', parent=menu.root_item)
        menu_item3 = MenuItem.objects.get(title='menu_item3', parent=menu.root_item)
        menu_item4 = MenuItem.objects.get(title='menu_item4', parent=menu.root_item)

        self.assertEquals(menu_item1.order, 0)
        self.assertEquals(menu_item2.order, 2)
        self.assertEquals(menu_item3.order, 1)
        self.assertEquals(menu_item4.order, 3)

        # Test forbidden move up
        self.assertRaises(MenuItem.DoesNotExist, lambda: move_item(menu_item1, -1))

    def test_move_down(self):
        menu = Menu(name='menu_move_down')
        menu.save()
        menu_item1 = MenuItem.objects.create(title='menu_item1', parent=menu.root_item)
        menu_item2 = MenuItem.objects.create(title='menu_item2', parent=menu.root_item)
        menu_item3 = MenuItem.objects.create(title='menu_item3', parent=menu.root_item)
        menu_item4 = MenuItem.objects.create(title='menu_item4', parent=menu.root_item)

        self.assertEquals(menu_item1.order, 0)
        self.assertEquals(menu_item2.order, 1)
        self.assertEquals(menu_item3.order, 2)
        self.assertEquals(menu_item4.order, 3)

        move_item(menu_item3, 1) # Move down

        # Retrieve objects from db
        menu_item1 = MenuItem.objects.get(title='menu_item1', parent=menu.root_item)
        menu_item2 = MenuItem.objects.get(title='menu_item2', parent=menu.root_item)
        menu_item3 = MenuItem.objects.get(title='menu_item3', parent=menu.root_item)
        menu_item4 = MenuItem.objects.get(title='menu_item4', parent=menu.root_item)

        self.assertEquals(menu_item1.order, 0)
        self.assertEquals(menu_item2.order, 1)
        self.assertEquals(menu_item3.order, 3)
        self.assertEquals(menu_item4.order, 2)

        # Test forbidden move up
        self.assertRaises(MenuItem.DoesNotExist, lambda: move_item(menu_item3, 1))

    def test_clean_children_order(self):
        menu = Menu(name='menu_clean_children_order')
        menu.save()
        menu_item1 = MenuItem.objects.create(title='menu_item1', parent=menu.root_item)
        menu_item2 = MenuItem.objects.create(title='menu_item2', parent=menu.root_item)
        menu_item3 = MenuItem.objects.create(title='menu_item3', parent=menu.root_item)
        menu_item4 = MenuItem.objects.create(title='menu_item4', parent=menu.root_item)

        # Initial check
        self.assertEquals(menu_item1.order, 0)
        self.assertEquals(menu_item2.order, 1)
        self.assertEquals(menu_item3.order, 2)
        self.assertEquals(menu_item4.order, 3)

        # Mess up order
        menu_item1.order = 99
        menu_item1.save()
        menu_item2.order = -150
        menu_item2.save()
        menu_item3.order = 3
        menu_item3.save()
        menu_item4.order = 67
        menu_item4.save()

        clean_order(menu.root_item.children())

        # Retrieve objects from db
        menu_item1 = MenuItem.objects.get(title='menu_item1', parent=menu.root_item)
        menu_item2 = MenuItem.objects.get(title='menu_item2', parent=menu.root_item)
        menu_item3 = MenuItem.objects.get(title='menu_item3', parent=menu.root_item)
        menu_item4 = MenuItem.objects.get(title='menu_item4', parent=menu.root_item)

        self.assertEquals(menu_item1.order, 3)
        self.assertEquals(menu_item2.order, 0)
        self.assertEquals(menu_item3.order, 1)
        self.assertEquals(menu_item4.order, 2)



    def test_move_item_or_clean_order(self):
        menu = Menu(name='menu_move_item_or_clean_order')
        menu.save()
        menu_item1 = MenuItem.objects.create(title='menu_item1', parent=menu.root_item)
        menu_item2 = MenuItem.objects.create(title='menu_item2', parent=menu.root_item)
        menu_item3 = MenuItem.objects.create(title='menu_item3', parent=menu.root_item)
        menu_item4 = MenuItem.objects.create(title='menu_item4', parent=menu.root_item)

        self.assertEquals(menu_item1.order, 0)
        self.assertEquals(menu_item2.order, 1)
        self.assertEquals(menu_item3.order, 2)
        self.assertEquals(menu_item4.order, 3)

        # Corrupt order
        menu_item1.order = 0
        menu_item1.save()
        menu_item2.order = 0
        menu_item2.save()
        menu_item3.order = 0
        menu_item3.save()
        menu_item4.order = 0
        menu_item4.save()

        move_item_or_clean_order(menu_item3, -1) # Move up

        # Retrieve objects from db
        menu_item1 = MenuItem.objects.get(title='menu_item1', parent=menu.root_item)
        menu_item2 = MenuItem.objects.get(title='menu_item2', parent=menu.root_item)
        menu_item3 = MenuItem.objects.get(title='menu_item3', parent=menu.root_item)
        menu_item4 = MenuItem.objects.get(title='menu_item4', parent=menu.root_item)

        self.assertEquals(menu_item1.order, 0)
        self.assertEquals(menu_item2.order, 2)
        self.assertEquals(menu_item3.order, 1)
        self.assertEquals(menu_item4.order, 3)

        # Corrupt order
        menu_item1.order = 18
        menu_item1.save()
        menu_item2.order = -1
        menu_item2.save()
        menu_item3.order = 6
        menu_item3.save()
        menu_item4.order = 99
        menu_item4.save()

        move_item_or_clean_order(menu_item1, 1) # Try to move down

        # Retrieve objects from db
        menu_item1 = MenuItem.objects.get(title='menu_item1', parent=menu.root_item)
        menu_item2 = MenuItem.objects.get(title='menu_item2', parent=menu.root_item)
        menu_item3 = MenuItem.objects.get(title='menu_item3', parent=menu.root_item)
        menu_item4 = MenuItem.objects.get(title='menu_item4', parent=menu.root_item)

        self.assertEquals(menu_item1.order, 3)
        self.assertEquals(menu_item2.order, 0)
        self.assertEquals(menu_item3.order, 1)
        self.assertEquals(menu_item4.order, 2)


class ContenttreePagesTestCase(TestCase):
    def test_pages_loadable(self):
        for item in MenuItem.objects.all():
            url = item.get_url()
            if url:
                response = self.client.get(url)
                self.assertTrue(response.status_code in [200, 301, 302])

